/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.seari.ats_iscs;

import com.seari.ats_iscs.handler.AtsIscsDecoder;
import com.seari.ats_iscs.handler.AtsIscsHandler;
import com.seari.bean.BaseClient;
import com.seari.bean.ats_iscs.AtsIscsMessageTypeEnum;
import com.seari.commonutils.common.DataUtils;
import com.seari.processor.BaseMessageListener;
import com.seari.processor.ChannelChecker;
import com.seari.utils.DataUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.concurrent.ScheduledFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;

/**
 * @author Rainbow
 */
public class AtsIscsClient extends BaseClient {

    protected static Logger logger = LoggerFactory.getLogger(AtsIscsClient.class);

    public static byte[] identityNumber = new byte[] {0,0,0,0}; //默认身份识别号

//    private AtsIscsTimer timer;

    private ScheduledFuture scheduledFuture;

    private BaseMessageListener listener; //外部消息监听接口
    
    private AtsIscsHandler atsIscsHandler;

    public AtsIscsClient() {
        super();
    }

    public AtsIscsClient(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public static void main(String[] args) throws InterruptedException {
        AtsIscsClient client = new AtsIscsClient("127.0.0.1",2404);
        client.setup();
        ChannelChecker channelChecker = new ChannelChecker(client);
        new Thread(channelChecker).start();
    }

    @Override
    public void setup() throws InterruptedException {
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        AtsIscsClient client = this;
        try {
            Bootstrap b = new Bootstrap();
            atsIscsHandler = new AtsIscsHandler(client);
            b.group(workerGroup)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    .remoteAddress(new InetSocketAddress(host, port))
//                    .remoteAddress(new InetSocketAddress("221.133.243.172", 9620))
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast("atsIscsDecoder", new AtsIscsDecoder());
                            ch.pipeline().addLast("atsIscsHandler", atsIscsHandler);
                        }
                    });
            // Start the client.  
            ChannelFuture f = b.connect().sync();
            channel = f.channel();
            // Wait until the connection is closed.
            if (f.isSuccess()) {
                logger.info("连接成功");
            }
            //channel通道关闭的监听器，监听到channel关闭时执行监听器中的代码
            f.channel().closeFuture().addListener(new GenericFutureListener<ChannelFuture>() {
                @Override
                public void operationComplete(ChannelFuture f) throws Exception {
                    logger.info("ATS_ISCS client shutdown gracefully>>>>>>>>>>>>>>>>>");
                    //channel正常关闭时进行workergroup线程池关闭，释放redis连接
                    cleanUp();
                    workerGroup.shutdownGracefully();
                }
            });
        } catch (Exception ex) {
            logger.warn(ex.getMessage());
            try
            {
                logger.warn("exception occured when connecting to server,shutdown workergroup.....");
                //BootStrap连接主机失败时，此时Handler已实例化，channel未实例化，直接释放redis连接
                cleanUp();
                workerGroup.shutdownGracefully();
            } catch (Exception e)
            {
                e.printStackTrace();
            }
        }
//        finally {
//            workerGroup.shutdownGracefully();
//        }
    }

    @Override
    public void reset() throws InterruptedException {
        //检测线程重启此client之前，尝试关闭可能未释放的资源，如redis连接
        cleanUp();
//        timer.setTerminate(true); //重连前关闭定时器
        if(null != scheduledFuture)
        {
            scheduledFuture.cancel(true);
        }
        setup();
    }
    
    private void cleanUp()
    {
        try
        {
            if(null != atsIscsHandler)
            {
                atsIscsHandler.closeJedis();
            }
        } catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    /**
     * 解除订阅报文
     */
    public void removeSubscribe() {
        //设置报文头
        ByteBuf buffer = channel.alloc().buffer((4 + 2 + 2 + 4) * 4);
        buffer.writeBytes(AtsIscsHelper.ATS_ISCS_HEADER);
        //设置报文长度
        buffer.writeBytes(DataUtil.reverse(DataUtils.int16ToByteArray(6)));
        //设置报文类型
        buffer.writeBytes(DataUtil.reverse(DataUtil.short2byte(AtsIscsMessageTypeEnum.RELIEVE_SUBSCRIBE.getValue())));
        //添加身份识别号
        buffer.writeBytes(identityNumber);
        //发送报文
        channel.writeAndFlush(buffer);

        //停止线程
//        timer.setTerminate(true);
        scheduledFuture.cancel(true);
    }

    /**
     * 使用指定的channel发送ISCS设备故障消息
     *
     * @param message
     */
    public void sendISCSData(byte[] message) {
    	if (channel.isActive())
		{
	    	int size = 8; //消息长度，固定为10
	    	int messageType = 105; //报文类型，固定为105
	    	byte propertyID = 1; //对象属性ID，固定为1
	        ByteBuf buffer = channel.alloc().buffer(14 * 4); //总长度固定为14
	        //报文头
	        buffer.writeBytes(AtsIscsHelper.ATS_ISCS_HEADER);
	        //报文长度
	        buffer.writeBytes(DataUtil.reverse(DataUtils.int16ToByteArray(size)));
	        //报文类型
	        buffer.writeBytes(DataUtil.reverse(DataUtils.int16ToByteArray(messageType)));
	        //报文内容
	        buffer.writeBytes(DataUtil.reverse(DataUtils.int16ToByteArray(message[0]))); //message[0]为对象组ID
	        buffer.writeBytes(DataUtil.reverse(DataUtils.int16ToByteArray(message[1]))); //message[1]为对象ID
	        buffer.writeByte(propertyID); //对象属性ID，仅有1项属性
	        buffer.writeByte(message[2]); //message[2]为对象属性值
	        channel.writeAndFlush(buffer);
		}
    }
    
    /**
     * 发送ISCS指令至ATS系统
     * */
    public void sendOrderToAts(int[] message)
    {
    	if(channel.isActive())
    	{
    		int messageType = 104; //报文类型，固定为104
    		ByteBuf buffer = channel.alloc().buffer((message.length + 8) * 4);
    		//报文头
	        buffer.writeBytes(AtsIscsHelper.ATS_ISCS_HEADER);
	        //报文长度
	        buffer.writeBytes(DataUtil.reverse(DataUtils.int16ToByteArray((message.length * 2) + 4 + 2))); //额外加上4字节身份识别号,2字节报文类型
	        //报文类型
	        buffer.writeBytes(DataUtil.reverse(DataUtils.int16ToByteArray(messageType)));
	        //添加身份识别号
	        buffer.writeBytes(identityNumber);
	        //命令号
	        buffer.writeBytes(DataUtil.reverse(DataUtils.int16ToByteArray(message[0]))); //message[0]为命令号
	        switch (message[0])
			{
	        	/*
	        	 * 此处可进行case语法优化，但对性能无影响，采用此可读性较好写法
	        	 * */
				case 5://运行到站台
					buffer.writeBytes(DataUtil.reverse(DataUtils.int16ToByteArray(message[1]))); //message[1]为列车ID
					buffer.writeBytes(DataUtil.reverse(DataUtils.int16ToByteArray(message[2]))); //message[2]为站台ID
					break;
				case 6://运行到信号机
					buffer.writeBytes(DataUtil.reverse(DataUtils.int16ToByteArray(message[1]))); //message[1]为列车ID
					buffer.writeBytes(DataUtil.reverse(DataUtils.int16ToByteArray(message[2]))); //message[2]为信号机ID
					break;
				case 9://给列车分配运行线
					buffer.writeBytes(DataUtil.reverse(DataUtils.int16ToByteArray(message[1]))); //message[1]为列车ID
					buffer.writeBytes(DataUtil.reverse(DataUtils.int16ToByteArray(message[2]))); //message[2]为运行线ID
					break;
				case 11://设置/取消站台扣车
					buffer.writeBytes(DataUtil.reverse(DataUtils.int16ToByteArray(message[1]))); //message[1]为站台ID
					buffer.writeBytes(DataUtil.reverse(DataUtils.int16ToByteArray(message[2]))); //message[2]为站台状态
					break;
				case 12://设置/取消站台跳停
					buffer.writeBytes(DataUtil.reverse(DataUtils.int16ToByteArray(message[1]))); //message[1]为站台ID
					buffer.writeBytes(DataUtil.reverse(DataUtils.int16ToByteArray(message[2]))); //message[2]为跳停状态
					break;
				case 15://清客完毕
					buffer.writeBytes(DataUtil.reverse(DataUtils.int16ToByteArray(message[1]))); //message[1]为列车ID
					break;
				default:
					break;
			}
	        channel.writeAndFlush(buffer);
    	}

    }

    public BaseMessageListener getListener()
    {
        return listener;
    }

    public void setListener(BaseMessageListener listener)
    {
        this.listener = listener;
    }

//    public void setTimer(AtsIscsTimer timer){
//        this.timer = timer;
//    }

    public ScheduledFuture getScheduledFuture() {
        return scheduledFuture;
    }

    public void setScheduledFuture(ScheduledFuture scheduledFuture) {
        this.scheduledFuture = scheduledFuture;
    }

}
